package com.lindroy.androidplayer.utils

import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import com.elvishew.xlog.XLog
import com.lindroy.androidplayer.logic.model.*
import com.lindroy.androidplayer.logic.network.base.BaseResponse
import com.lindroy.androidplayer.logic.network.base.http.RequestException
import com.lindroy.androidplayer.logic.network.enums.LayoutState
import com.lindroy.androidplayer.logic.network.enums.Action
import com.lindroy.androidplayer.logic.network.enums.HttpError
import com.lindroy.androidplayer.logic.network.handleException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import java.net.UnknownHostException
import kotlin.coroutines.CoroutineContext

/**
 * @author Lin
 * @date 2022/2/24
 * @function 关联请求操作和页面状态的 LiveData
 */
class StateLiveData<T : Any>(
    context: CoroutineContext = Dispatchers.IO,
    fetchData: suspend () -> BaseResponse<T>
) {
    private val action = SingleLiveEvent<Action>()

    private val _data = SingleLiveEvent<ApiResponse<T>>()

    private val isRefreshingLiveData = SingleLiveEvent<Boolean>()

    val isRefresh = isRefreshingLiveData.mapSingle { it }

    val data: LiveData<BaseResponse<T>> = _data.mapSingle { it }

    val layoutState = action.switchMapSingle {
        flow {
            val result = fetchData()
            when (result.success) {
                true -> {
                    if (result.data == null || (result.data is List<*> && (result.data as List<*>).isEmpty())) {
                        _data.postValue(EmptyResponse())
                        emit(LayoutState.EMPTY)
                    } else {
                        _data.postValue(SuccessResponse(result.data!!))
                        emit(LayoutState.CONTENT)
                    }
                }
                false -> {
                    //服务器有响应，但是 success 为 false，暂视为 SERVER_ERROR
                    _data.postValue(FailureResponse(handleException(RequestException(result))))
                    emit(
                        if (action.value == Action.Refresh) {
                            LayoutState.CONTENT
                        } else {
                            when (result.errorCode) {
                                HttpError.NOT_LOGIN.code -> LayoutState.NOT_LOGIN
                                else -> LayoutState.SERVER_ERROR

                            }
                        }
                    )
                }
            }
        }.onStart {
            _data.postValue(StartResponse())
            if (action.value == Action.Load) {
                emit(LayoutState.LOADING)
            }
        }.onCompletion {
            isRefreshingLiveData.postValue(false)
            _data.postValue(CompletionResponse())
        }.catch { throwable ->
            _data.postValue(FailureResponse(handleException(throwable)))
            emit(if (throwable is UnknownHostException) LayoutState.NO_NETWORK else LayoutState.SERVER_ERROR)
        }.asLiveData(Dispatchers.IO + context)
    }

    fun retry() {
        action.postValue(Action.Retry)
    }

    fun refresh() {
        action.postValue(Action.Refresh)
    }

    fun load() {
        action.postValue(Action.Load)
    }
}

